use std::fmt::{Debug, Display};
use std::fs::OpenOptions;
use std::time::Duration;
use chrono::{Local, Utc};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use tokio::sync::mpsc::{channel, Sender};
use tokio::time;
use tokio::time::Instant;

/**
 * @author Almas software technologies muhtarjaan mahmood (مۇختەرجان مەخمۇت)
 * @email ug-project@outlook.com
 * @create date 2022-07-15 14:45:36
 * @modify date 2022-07-15 14:45:36
 * @desc [description]
 */

pub trait Logger {
    fn info<T: Display>(&self, info: T);
    fn error<T: Display>(&self, info: T);
    fn ok<T: Display>(&self, info: T);
}

pub struct FileLogger {
    pub file_name: String,
    sender: Sender<Option<String>>
}
impl FileLogger {
    pub async fn new(file_name: String) -> FileLogger {
        let (sender, mut receiver) = channel(1024);
        let mut logger = FileLogger {
            sender,
            file_name: file_name.clone(),
        };
        tokio::spawn(async move {
            let mut file = tokio::fs::OpenOptions::new()
                .write(true)
                .read(false)
                .create(true)
                .append(true)
                .open(file_name.as_str()).await.expect(format!("Cannot create log file '{}'", file_name).as_str());
            while let Some(Some(line)) = receiver.recv().await {
                file.write_all(line.as_bytes()).await.expect("Cannot write log file");
                file.flush().await.expect("Cannot flush log file");
            }
        });
        logger
    }
    fn println<T : Display>(&self, tag: &str, message: T) {
        let line = format!("[{}] [{}] {}\n", Local::now().format("%Y/%m/%d %H:%M:%S:%3f GMT%:z"), tag, message);
        let sender = self.sender.clone();
        tokio::spawn(async move {
            sender.send(Some(line)).await.expect("Cannot send log file to log writer.");
        });
    }
}
impl Drop for FileLogger {
    fn drop(&mut self) {
        let sender = self.sender.clone();
        tokio::spawn(async move {
            sender.send(None).await.expect("Cannot close file writer channel.");
        });
    }
}
impl Logger for FileLogger {
    fn info<T: Display>(&self, info: T) {
        self.println("INFO", info);
    }

    fn error<T: Display>(&self, info: T) {
        self.println("ERROR", info);
    }

    fn ok<T: Display>(&self, info: T) {
        self.println("OK", info);
    }
}

pub struct TerminalLogger {}
impl TerminalLogger {
    pub async fn new() -> TerminalLogger {
        TerminalLogger{}
    }
    fn println<T : Display>(&self, tag: &str, message: T) {
        println!("[{}] [{}] {}", Local::now().format("%Y/%m/%d %H:%M:%S:%3f GMT+%:z"), tag, message);
    }
}

impl Logger for TerminalLogger {
    fn info<T: Display>(&self, info: T) {
        self.println("INFO", info);
    }

    fn error<T: Display>(&self, info: T) {
        self.println("ERROR", info);
    }

    fn ok<T: Display>(&self, info: T) {
        self.println("OK", info);
    }
}
